#include <string>
#include <cstdlib>
#include <ctime>
#include <iostream>

#include "snake.h"


SnakeBody::SnakeBody()
{
}


SnakeBody::SnakeBody(int x, int y): mX(x), mY(y)
{
}

int SnakeBody::getX() const
{
    return mX;
}

int SnakeBody::getY() const
{
    return mY;
}

bool SnakeBody::operator == (const SnakeBody& snakeBody) const
{
    return (this->getX() == snakeBody.getX() && this->getY() == snakeBody.getY());
}

void SnakeBody::setBonusDirection(Direction direction)
{
    this->mBonusDirection = static_cast<int>(direction);
}

Direction SnakeBody::getBonusDirection() const
{
    return static_cast<Direction>(this->mBonusDirection);
}

bool SnakeBody::hasBonusDirection() const
{
    return (this->mBonusDirection != -1);
}

Snake::Snake(int gameBoardWidth, int gameBoardHeight, int initialSnakeLength): mGameBoardWidth(gameBoardWidth), mGameBoardHeight(gameBoardHeight), mInitialSnakeLength(initialSnakeLength)
{
    this->initializeSnake();
    this->setRandomSeed();
}

void Snake::setRandomSeed()
{
    // use current time as seed for random generator
    std::srand(std::time(nullptr));
}

void Snake::initializeSnake()
{
    // Instead of using a random initialization algorithm
    // We always put the snake at the center of the game mWindows
    int centerX = this->mGameBoardWidth / 2;
    int centerY = this->mGameBoardHeight / 2;

    for (int i = 0; i < this->mInitialSnakeLength; i ++)
    {
        this->mSnake.push_back(SnakeBody(centerX, centerY + i));
    }
    this->mDirection = Direction::Up;
}

void Snake::setPosition(int x, int y)
{
    if (!this->mSnake.empty()) {
        this->mSnake[0] = SnakeBody(x, y);
        // 重新初始化蛇身
        this->mSnake.resize(this->mInitialSnakeLength);
        for (int i = 1; i < this->mInitialSnakeLength; i++) {
            this->mSnake[i] = SnakeBody(x, y + i);
        }
    }
}

void Snake::setDirection(Direction newDirection)
{
    this->mDirection = newDirection;
}

bool Snake::isPartOfSnake(int x, int y)
{
    SnakeBody temp = SnakeBody(x, y);
    for (int i = 0; i < this->mSnake.size(); i ++)
    {
        if (this->mSnake[i] == temp)
        {
            return true;
        }
    }
    return false;
}

/*
 * Assumption:
 * Only the head would hit wall.
 */
bool Snake::hitWall()
{
    SnakeBody& head = this->mSnake[0];
    int headX = head.getX();
    int headY = head.getY();
    if (headX <= 0 || headX >= this->mGameBoardWidth - 1)
    {
        return true;
    }
    if (headY <= 0 || headY >= this->mGameBoardHeight - 1)
    {
        return true;
    }
    return false;
}

/*
 * The snake head is overlapping with its body
 */
bool Snake::hitSelf()
{
    SnakeBody& head = this->mSnake[0];
    // Exclude the snake head
    for (int i = 1; i < this->mSnake.size(); i ++)
    {
        if (this->mSnake[i] == head)
        {
            return true;
        }
    }
    return false;
}

SnakeBody Snake::getHead() const
{
    if (!this->mSnake.empty()) {
        return this->mSnake[0];
    }
    return SnakeBody(0, 0);
}

bool Snake::touchFood()
{
    SnakeBody newHead = this->createNewHead();
    if (this->mFood == newHead)
    {
        return true;
    }
    else
    {
        return false;
    }
}

void Snake::senseFood(SnakeBody food)
{
    this->mFood = food;
}

std::vector<SnakeBody>& Snake::getSnake()
{
    return this->mSnake;
}

bool Snake::changeDirection(Direction newDirection)
{
    // 禁止直接180度掉头。
    if ((this->mDirection == Direction::Up && newDirection == Direction::Down) ||
        (this->mDirection == Direction::Down && newDirection == Direction::Up) ||
        (this->mDirection == Direction::Left && newDirection == Direction::Right) ||
        (this->mDirection == Direction::Right && newDirection == Direction::Left))
    {
        return false;
    }

    // 禁止间接掉头（防止撞上脖子）。
    // 这个检查只在蛇身长度大于1时才有意义。
    if (mSnake.size() > 1)
    {
        // "预计算"出如果按新方向移动，蛇头将要到达的新坐标
        SnakeBody potentialNewHead = this->getHead();
        int x = potentialNewHead.getX();
        int y = potentialNewHead.getY();

        switch (newDirection){
       
            case Direction::Up:    y--; break;
            case Direction::Down:  y++; break;
            case Direction::Left:  x--; break;
            case Direction::Right: x++; break;
            
        }
        potentialNewHead = SnakeBody(x, y);

        // 获取“脖子”（蛇的第二节身体）的坐标
        SnakeBody neck = this->mSnake[1];

        // 如果新方向会导致头撞到脖子，那么这次转向就是非法的，拒绝执行。
        if (potentialNewHead == neck)
        
        {
            return false;
           
            }
    
    
        
            
    }
    
    // 如果所有检查都通过了，才真正改变蛇的移动方向。
    this->mDirection = newDirection;
    return true;
}

SnakeBody Snake::createNewHead()
{
    SnakeBody& head = this->mSnake[0];
    int headX = head.getX();
    int headY = head.getY();
    int headXNext;
    int headYNext;

    switch (this->mDirection)
    {
        case Direction::Up:
        {
            headXNext = headX;
            headYNext = headY - 1;
            break;
        }
        case Direction::Down:
        {
            headXNext = headX;
            headYNext = headY + 1;
            break;
        }
        case Direction::Left:
        {
            headXNext = headX - 1;
            headYNext = headY;
            break;
        }
        case Direction::Right:
        {
            headXNext = headX + 1;
            headYNext = headY;
            break;
        }
    }

    SnakeBody newHead = SnakeBody(headXNext, headYNext);

    return newHead;
}

/*
 * If eat food, return true, otherwise return false
 */
bool Snake::moveFoward()
{
    if (this->touchFood())
    {
        SnakeBody newHead = this->mFood;
        this->mSnake.insert(this->mSnake.begin(), newHead); 
        return true;
    }
    else
    {
        this->mSnake.pop_back();
        SnakeBody newHead = this->createNewHead();
        this->mSnake.insert(this->mSnake.begin(), newHead); 
        return false;
    }
}

bool Snake::checkCollision()
{
    if (this->hitWall() || this->hitSelf())
    {
        return true;
    }
    else
    {
        return false;
    }
}


int Snake::getLength()
{
    return this->mSnake.size();
}

bool Snake::eatFromBonusDirection()
{
    if (!this->touchFood())
    {
        return false;
    }

    if (!this->mFood.hasBonusDirection())
    {
        return false;
    }

    return (this->mDirection == this->mFood.getBonusDirection());
}

Direction Snake::getCurrentDirection() const
{
    return this->mDirection;
}

bool Snake::checkObstacleCollision(const std::vector<SnakeBody>& obstacles) const
{
    // 蛇头
    const SnakeBody& head = this->mSnake[0];
    for (const auto& obs : obstacles) {
        if (head == obs) {
            return true;
        }
    }
    return false;
}

SnakeBody Snake::dropTail(int &maxDrop)
{
    if (maxDrop <= 0)
    {
        maxDrop = mSnake.size() / 2;
    }
    // 为防止蛇完全消失，设置一个最小长度限制
    if (mSnake.size() > maxDrop)
    {
        SnakeBody droppedPart = mSnake.back(); // 获取最后一节蛇身
        mSnake.pop_back();                     // 将其从蛇身中移除
        return droppedPart;                    // 返回被移除的蛇身部分
    }
    // 如果蛇太短，则返回一个无效坐标，表示技能使用失败
    maxDrop = -1;
    return SnakeBody(-1, -1); 
}

void Snake::shorten()
{
    // 只有当蛇的长度大于1时才缩短，防止蛇完全消失
    if (this->mSnake.size() > 1)
    {
        this->mSnake.pop_back();
    }
}